package com.extremelylargepackage.sprite;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.util.Log;

import com.extremelylargepackage.GameResources;
import com.extremelylargepackage.level.LevelParser;

public class AnimationParser {
	// TODO: fixed size cache? see http://littletechsecrets.wordpress.com/2008/11/16/simple-lru-cache-in-java/
	private static Map<String,Animation> animCache = new HashMap<String,Animation>();

	private AnimationParser()
	{
		throw new IllegalStateException("AnimationParser cannot be instantiated");
	}

	public static Animation parseAnimation(String animName)
			throws IOException, SAXException, MalformedAnimationException
	{
		if(animCache.containsKey(animName))
			return (Animation) animCache.get(animName).clone();

		Animation anim = new Animation(animName);

		InputStream levelInput = GameResources.getAssets().open("animation/" + animName + ".ani");

		Document doc = null;
		try {
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			doc = dBuilder.parse(levelInput);
		} catch (ParserConfigurationException pce) {
			Log.e("Error parsing animation!", "", pce);
			return null;
		}

		Element root = doc.getDocumentElement();
		root.normalize();

		if(!root.getNodeName().equals("animation")) {
			throw new MalformedAnimationException("Root node must be of type \"animation\"");
		}

		anim.setRepeat(root.getAttribute("repeat").equals("true")); // default to false
		anim.setCentered(!root.getAttribute("centered").equals("false")); // default to true

		NodeList nodes = root.getChildNodes();
		for(int i = 0; i < nodes.getLength(); ++i) {
			Node n = nodes.item(i);
			if(n.getNodeType() == Node.ELEMENT_NODE) {
				Element elem = (Element)n;
				if(n.getNodeName().equals("frame")) {
					AbstractSprite sprite = GameResources.getResource(elem.getAttribute("sprite"));
					if(sprite == null) {
						throw new MalformedAnimationException("Missing or invalid sprite name for frame node.");
					}

					long duration;
					try {
						duration = LevelParser.parseTimeValue(elem.getAttribute("duration"));
					} catch(IllegalArgumentException iae) {
						throw new MalformedAnimationException("Illegal value for duration in frame node.", iae);
					}

					if(duration < 0) {
						throw new MalformedAnimationException("Frame duration must be positive.");
					}

					anim.addFrame(sprite, duration);
				} else {
					throw new MalformedAnimationException("Unrecognized node type \"" + n.getNodeName() + "\"");
				}
			}
		}

		animCache.put(animName, anim);

		return anim;
	}
}
